package com.platform.netty.tcp.handler;

import com.platform.common.manager.AsyncManager;
import com.platform.common.utils.netty.ChannelSupervise;
import com.platform.common.utils.netty.NettyUtil;
import com.platform.common.utils.redis.RedisUtil;
import com.platform.netty.tcp.task.CalorPoolTask;
import com.platform.netty.tcp.task.CentraPoolTask;
import com.platform.netty.tcp.task.AlarmPoolTask;
import com.platform.netty.tcp.task.ValvePoolTask;
import io.netty.channel.*;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.net.InetSocketAddress;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;


/**
 * @author: [青衫] 'QSSSYH@QQ.com'
 * @Date: 2019-07-08 10:47
 * @Description: < 描述 >
 */
@Component
@ChannelHandler.Sharable
@Slf4j
public class TcpServerHandler extends ChannelInboundHandlerAdapter {

	@Autowired
	private RedisUtil redisUtil;

	/**
	 * 活跃的、有效的通道
	 * 第一次连接成功后进入的方法
	 *
	 * @param ctx
	 * @throws Exception
	 */
	@Override
	public void channelActive(ChannelHandlerContext ctx) throws Exception {
		super.channelActive(ctx);
		String ip = getRemoteAddress(ctx);
		if(!redisUtil.hHasKey("netty-error",ip)){
			String channelId = ctx.channel().id().asLongText();
			log.info("与tcp客户端" + getRemoteAddress(ctx) + "建立连接，通道开启！channelActive 被调用" + channelId);
			//将tcp客户端的channel信息保存
			ChannelSupervise.addChannel(ctx.channel());
		}else{
			ctx.disconnect();
		}
	}

	/**
	 * 不活动的通道
	 * 连接丢失后执行的方法（client端可据此实现断线重连）
	 *
	 * @param ctx
	 * @throws Exception
	 */
	@Override
	public void channelInactive(ChannelHandlerContext ctx) {
		String channelId = ctx.channel().id().asLongText();
		//根据通道id获取采集器id
		String clientId = "";
		if(redisUtil.hHasValue("sys-netty", channelId)){
			clientId = redisUtil.hgetValue("sys-netty", channelId).toString();
			//移除redis中保存的channel
			redisUtil.hdel("sys-netty", clientId);
			//保存告警信息
			AsyncManager.me().execute(AlarmPoolTask.saveAlarm(clientId,"3","2","采集器失去连接", "采集器"+clientId+"失去链接，通道关闭"));

			log.info("与tcp客户端" + clientId + "失去连接，通道关闭！channelInactive 被调用" + channelId);
		}
		//移除tcp客户端的channel信息
		ChannelSupervise.removeChannel(ctx.channel());
		//断开连接
		ctx.disconnect();
	}

	/**
	 * 拿到传过来的msg数据，开始处理
	 *
	 * @param ctx
	 * @param msg
	 * @throws Exception
	 */
	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg) {
		//获取管道id
		String channelId = ctx.channel().id().asLongText();
		Channel channel = ChannelSupervise.findChannel(channelId);
		String start = msg.toString().substring(0,2);
		String end = msg.toString().substring(msg.toString().length()-2);
		if(!start.equals("BB")){
			if(redisUtil.hHasKey(channelId, "calor_datas")){
				//单采数据
				String datas = redisUtil.hget(channelId, "calor_datas").toString() + " " + msg.toString();
				if(end.equals("F0")){
					redisUtil.hdel(channelId, "calor_datas");
					String[] msgs = datas.split(" ");
					String address = "";
					//数据长度
					int datalength = NettyUtil.get10HexNum(msgs[9] + msgs[10]);
					log.info("数据长度为："+datalength);
					if(msgs[3].equals("00") && msgs[4].equals("00")){
						//阀门
						address = msgs[6]+msgs[5];
						datas = NettyUtil.strChange(msgs,11,datalength);
						this.saveValve(datas, null);
					}else{
						//热表
						address = msgs[3]+msgs[4]+msgs[5]+msgs[6];
						datas = NettyUtil.strChange(msgs,11,datalength);
						this.saveCalor(datas, null);
					}
					redisUtil.hset(address, "01", "00",10);
				}else{
					redisUtil.hset(channelId, "calor_datas", datas);
				}
			}else if(redisUtil.hHasKey(channelId, "concentrator_data")){
				//批量获取数据
				log.info("Tcp服务端接收到" + channelId + "的批量采集数据: " + msg.toString());
				String datas = redisUtil.hget(channelId, "concentrator_data").toString() + " " + msg.toString();
				if(end.equals("F0")){
					redisUtil.hdel(channelId, "concentrator_data");
					String[] msgs = datas.split(" ");
					//数据长度
					int datalength = NettyUtil.get10HexNum(msgs[9] + msgs[10]);
					log.info("数据长度为："+datalength);
					String clientId = msgs[2] + msgs[1];
					if(msgs[3].equals("00") && msgs[4].equals("00")){
						//阀门
						datas = NettyUtil.strChange(msgs,11,datalength);
						this.saveValve(datas, null);
					}else{
						//热表
						datas = NettyUtil.strChange(msgs,11,datalength);
						this.saveCalor(datas, null);
					}
					redisUtil.hset(clientId, "05", "00",10);
				}else{
					redisUtil.hset(channelId, "concentrator_data", datas);
				}
			}else if(redisUtil.hHasKey(channelId, "concentrator_type")){
				//获取采集器关联设备信息
				log.info("Tcp服务端接收到" + channelId + "的关联设备数据: " + msg.toString());
				String datas = redisUtil.hget(channelId, "concentrator_type").toString() + " " + msg.toString();
				if(end.equals("F0")){
					redisUtil.hdel(channelId, "concentrator_type");
					//热表数据
					this.saveLink(datas);
				}else{
					redisUtil.hset(channelId, "concentrator_type", datas);
				}
			}else if(redisUtil.hHasKey(channelId, "concentrator_offline")){
				//获取离线采集数据
				log.info("Tcp服务端接收到" + channelId + "的离线采集数据: " + msg.toString());
				String datas = redisUtil.hget(channelId, "concentrator_offline").toString() + " " + msg.toString();
				if(end.equals("F0")){
					redisUtil.hdecr(channelId,"10", 1);
					redisUtil.hdel(channelId, "concentrator_offline");
					String[] msgs = datas.split(" ");
					String time = "20" + msgs[11] + "-" + msgs[12] + "-" + msgs[13] + " " + msgs[14] + ":" + msgs[15];
					SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");
					Date createTime = null;
					try {
						createTime = dateFormat.parse(time);
					} catch (ParseException e) {
						e.printStackTrace();
					}
					//数据长度
					int datalength = NettyUtil.get10HexNum(msgs[9] + msgs[10]);
					log.info("数据长度为："+datalength);
					datas = NettyUtil.strChange(msgs,22,datalength-11);
					if(msgs[3].equals("00") && msgs[4].equals("00")){
						//阀门
						this.saveValve(datas, createTime);
					}else{
						//热表
						this.saveCalor(datas, createTime);
					}
				}else{
					redisUtil.hset(channelId, "concentrator_offline", datas);
				}
			}else{
				log.info("tcp客户端："+getRemoteAddress(ctx)+"的非法数据："+msg.toString());
				if(!redisUtil.hHasValue("sys-netty",channelId)){
					//关闭客户端
					ctx.disconnect();
					redisUtil.hset("netty-error", getRemoteAddress(ctx), ctx.channel().id().asShortText());
				}
			}
			return;
		}
		log.info("Tcp服务端接收到" + channelId + "的数据: " + msg.toString());
		//将客户端数据转换成数组格式
		String[] msgs = msg.toString().split(" ");
		//采集器id
		String clientId = msgs[2] + msgs[1];
		String address = "";
		String addressType = "";
		if(msgs[3].equals("00") && msgs[4].equals("00")){
			//阀门id
			address = msgs[6]+msgs[5];
			addressType = "1";
		}else{
			//热表id
			address = msgs[3]+msgs[4]+msgs[5]+msgs[6];
			addressType = "2";
		}
		//控制码
		String cont = msgs[7];
		//错误码
		String errcode = msgs[8];
		if(errcode.equals("00")) {
			//数据长度
			int datalength = NettyUtil.get10HexNum(msgs[9] + msgs[10]);
			log.info("数据长度为："+datalength);
			if(cont.equals("11")){
				//这里是心跳包,建立采集器与服务器链路
				//将采集器信息和管道信息存放到redis中
				redisUtil.hset("sys-netty", clientId, channelId);
				//计算校验码
				int ll = NettyUtil.get10HexNum(msgs[1])+NettyUtil.get10HexNum(msgs[2])+NettyUtil.get10HexNum(cont);
				String check = NettyUtil.get16HexNum(ll);
				if(check.length()<2){
					check = "0" + check;
				}else{
					check = check.substring(check.length()-2);
				}
				String response = "AA " + msgs[1] + " " + msgs[2] + " 00 00 00 00 " + cont + " 00 00 00 00 00 00 " + check + " FF ";
				String revertStr = response.replace(" ", "");
				NettyUtil.writeToClient(revertStr, channel, "心跳数据");
			}else if(cont.equals("01")){
				//这里是查询数据结果
				if(end.equals("F0")){
					String datas = NettyUtil.strChange(msgs,11,datalength);
					if(addressType.equals("1")){
						this.saveValve(datas, null);
					}else{
						this.saveCalor(datas, null);
					}
					redisUtil.hset(address,cont,errcode,10);
				}else{
					redisUtil.hset(channelId, "calor_datas", msg.toString());
				}
			}else if(cont.equals("02")){
				//这里是设置阀门开度
				redisUtil.hset(address,cont,errcode, 10);
			}else if(cont.equals("03")){
				//这里是设备登记
				redisUtil.hset(address,cont,errcode, 10);
			}else if(cont.equals("04")){
				//这里是设备注销
				redisUtil.hset(address,cont,errcode, 10);
			}else if(cont.equals("05")){
				//这里是批量设备获取数据
				if(end.equals("F0")){
					String datas = NettyUtil.strChange(msgs,11,datalength);
					if(addressType.equals("1")){
						this.saveValve(datas, null);
					}else{
						this.saveCalor(datas, null);
					}
					redisUtil.hset(clientId,cont,errcode,10);
				}else{
					redisUtil.hset(channelId, "concentrator_data", msg.toString());
				}
			}else if(cont.equals("06")){
				//这里是批量设置阀开度
				redisUtil.hset(clientId,cont,errcode, 10);
			}else if(cont.equals("07")){
				//这里是离线设置抄表时间
				redisUtil.hset(clientId,cont,errcode, 10);
			}else if(cont.equals("08")){
				//这里是设置心跳时间
				redisUtil.hset(clientId,cont,errcode, 10);
			}else if(cont.equals("09")){
				//这里是限制阀门开启
				redisUtil.hset(address,cont,errcode, 10);
			}else if(cont.equals("0A")){
				//这里是采集器初始化
				this.saveInit(clientId);
				redisUtil.hset(clientId,cont,errcode, 10);
			}else if(cont.equals("0B")){
				//这里是校准时间
				redisUtil.hset(clientId,cont,errcode, 10);
			}else if(cont.equals("0C")){
				//这里是获取采集器关联设备信息
				if(end.equals("F0")){
					//保存设备类型
					this.saveLink(msg.toString());
				}else{
					//保存到redis
					redisUtil.hset(channelId, "concentrator_type", msg.toString());
				}
			}else if(cont.equals("0D")){
				//这里是查看采集器时间
				//根据数据长度获取数据内容
				String datas = NettyUtil.strChange(msgs,11,datalength);
				String[] dd = datas.split(" ");
				String time = "20"+dd[0]+"-"+dd[1]+"-"+dd[2]+" "+dd[3]+":"+dd[4];
				log.info(time);
				//将数据保存redis
				redisUtil.hset(clientId,cont,time, 10);
			}else if(cont.equals("0E")){
				//这里是批量操作结束符
				int datacount = (Integer)redisUtil.hget(channelId,"10");
				if(datacount==0){
					redisUtil.hdel(channelId,"10");
					//计算校验码
					int ll = NettyUtil.get10HexNum(msgs[1])+NettyUtil.get10HexNum(msgs[2])+NettyUtil.get10HexNum(cont);
					String check = NettyUtil.get16HexNum(ll);
					if(check.length()<2){
						check = "0" + check;
					}else{
						check = check.substring(check.length()-2);
					}
					String response = "AA " + msgs[1] + " " + msgs[2] + " 00 00 00 00 " + cont + " 00 00 00 00 00 00 " + check + " FF ";
					String revertStr = response.replace(" ", "");
					NettyUtil.writeToClient(revertStr, channel, "数据接收完毕");
				}
			}else if(cont.equals("0F")){
				//这里是MUBS过载
				redisUtil.hset(clientId, cont, "1");
				log.info("tcp客户端" + getRemoteAddress(ctx) + "MBUS过载");
				//计算校验码
				int ll = NettyUtil.get10HexNum(msgs[1])+NettyUtil.get10HexNum(msgs[2])+NettyUtil.get10HexNum(cont);
				String check = NettyUtil.get16HexNum(ll);
				if(check.length()<2){
					check = "0" + check;
				}else{
					check = check.substring(check.length()-2);
				}
				String response = "AA " + msgs[1] + " " + msgs[2] + " 00 00 00 00 " + cont + " 00 00 00 00 00 00 " + check + " FF ";
				String revertStr = response.replace(" ", "");
				NettyUtil.writeToClient(revertStr, channel, "MBUS过载警告");
				//保存告警信息
				AsyncManager.me().execute(AlarmPoolTask.saveAlarm(clientId, "3","2","MBUS过载", "采集器"+clientId+"MBUS过载"));
			}else if(cont.equals("10")){
				//获取数据内容
				String[] datass = NettyUtil.strsChange(msgs,11,datalength);
				int datacount = 0;
				for(int i=0;i<datass.length;i++){
					int m = 11 + i;
					datacount += NettyUtil.get10HexNum(msgs[m]);
				}
				log.info("tcp客户端" + getRemoteAddress(ctx) + "总共"+datacount+"条离线数据待传输");
				//将总数居记录保存到redis
				redisUtil.hset(channelId, cont, datacount);
				log.info("tcp客户端" + getRemoteAddress(ctx) + "返回开始接收");
				//计算校验码
				int ll = NettyUtil.get10HexNum(msgs[1])+NettyUtil.get10HexNum(msgs[2])+NettyUtil.get10HexNum(cont);
				String check = NettyUtil.get16HexNum(ll);
				if(check.length()<2){
					check = "0" + check;
				}else{
					check = check.substring(check.length()-2);
				}
				String response = "AA " + msgs[1] + " " + msgs[2] + " 00 00 00 00 " + cont + " 00 00 00 00 00 00 " + check + " FF ";
				String revertStr = response.replace(" ", "");
				NettyUtil.writeToClient(revertStr, channel, "离线传输数据开始");
			}else if(cont.equals("12")){
				//这里是获取离线设备数据
				if(end.equals("F0")){
					redisUtil.hdecr(channelId, "10", 1);
					String time = "20" + msgs[11] + "-" + msgs[12] + "-" + msgs[13] + " " + msgs[14] + ":" + msgs[15];
					SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");
					Date createTime = null;
					try {
						createTime = dateFormat.parse(time);
					} catch (ParseException e) {
						e.printStackTrace();
					}
					String datas = NettyUtil.strChange(msgs,22,datalength-11);
					if(addressType.equals("1")){
						this.saveValve(datas, createTime);
					}else{
						this.saveCalor(datas, createTime);
					}
				}else{
					//保存到redis
					redisUtil.hset(channelId, "concentrator_offline", msg.toString());
				}
			}else if(cont.equals("13")){
				//这里是修改服务器地址
				redisUtil.hset(clientId,cont,errcode, 10);
				redisUtil.hdel("sys-netty",clientId);
			}
		}else{
			log.info("tcp客户端" + clientId + "数据有误");
			String alarmMsg = "";
			if(errcode.equals("01")){
				alarmMsg = "设备已存在";
			}else if(errcode.equals("02")){
				alarmMsg = "设备不存在或无法通信";
			}else if(errcode.equals("03")){
				alarmMsg = "热表无法通信";
			}else if(errcode.equals("04")){
				alarmMsg = "不支持该类型设备";
			}

			AsyncManager.me().execute(AlarmPoolTask.saveAlarm(address, addressType,"1","数据采集", alarmMsg));

			redisUtil.hset(address, cont, errcode, 10);
		}
	}

	/**
	 * 服务端接收客户端发送过来的数据结束之后调用
	 * @param ctx
	 * @throws Exception
	 */
	@Override
	public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
		super.channelReadComplete(ctx);
		log.info("tcp客户端" + getRemoteAddress(ctx) + "数据读取完成！");
	}

	/**
	 * 异常处理
	 *
	 * @param ctx
	 * @param cause
	 * @throws Exception
	 */
	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		super.exceptionCaught(ctx, cause);
		if(ctx.channel().isActive()){
			//发生异常，关闭连接
			log.error("引擎 {} 的通道发生异常，即将断开连接", getRemoteAddress(ctx));
			String channelId = ctx.channel().id().asLongText();
			String clientId = "";
			if(redisUtil.hHasValue("sys-netty", channelId)){
				clientId = redisUtil.hgetValue("sys-netty", channelId).toString();
				//移除redis中保存的channel
				redisUtil.hdel("sys-netty", clientId);
				//保存告警信息
				AsyncManager.me().execute(AlarmPoolTask.saveAlarm(clientId, "3","2","采集器异常", "采集器"+clientId+"失去连接，通道关闭"));
			}
			//移除tcp客户端的channel信息
			ChannelSupervise.removeChannel(ctx.channel());
			//断开连接
			ctx.disconnect();
		}
	}

	/**
	 * 心跳机制，超时处理
	 *
	 * @param ctx
	 * @param evt
	 * @throws Exception
	 */
	@Override
	public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
		String socketString = ctx.channel().remoteAddress().toString();
		if (evt instanceof IdleStateEvent) {
			IdleStateEvent event = (IdleStateEvent) evt;
			if (event.state() == IdleState.READER_IDLE) {
				log.info("Client: " + socketString + " READER_IDLE 读超时");
				ctx.disconnect();//断开
			} else if (event.state() == IdleState.WRITER_IDLE) {
				log.info("Client: " + socketString + " WRITER_IDLE 写超时");
				ctx.disconnect();
			} else if (event.state() == IdleState.ALL_IDLE) {
				log.info("Client: " + socketString + " ALL_IDLE 总超时");
				ctx.disconnect();
			}
		}
	}

	/**
	 * 获取client对象：ip
	 *
	 * @param ctx
	 * @return
	 */
	private String getRemoteAddress(ChannelHandlerContext ctx) {
		InetSocketAddress insocket = (InetSocketAddress) ctx.channel().remoteAddress();
		String ip = insocket.getAddress().getHostAddress();
		return ip;
	}

	/**
	 * 异步保存阀门数据
	 * @param datas
	 */
	private void saveValve(String datas, Date createTime){
		log.info("接收到阀门数据："+datas);
		AsyncManager.me().execute(ValvePoolTask.saveValve(datas, createTime));
	}

	/**
	 * 异步保存热表数据
	 * @param datas
	 */
	private void saveCalor(String datas, Date createTime){
		log.info("接收到热表数据："+datas);
		AsyncManager.me().execute(CalorPoolTask.saveCalor(datas, createTime));
	}

	/**
	 * 异步保存采集器关联设备信息
	 * @param datas
	 */
	private void saveLink(String datas){
		AsyncManager.me().execute(CentraPoolTask.saveCentra(datas, 1));
	}

	/**
	 * 异步保存采集器初始化信息
	 * @param clientId
	 */
	private void saveInit(String clientId){
		AsyncManager.me().execute(CentraPoolTask.saveCentra(clientId, 2));
		redisUtil.hdel("sys-netty", clientId);
	}

}
